package com.woniu.customer.filter;

import com.woniu.common.entity.AdmCustomer;
import com.woniu.customer.configuration.JWTToken;
import com.woniu.customer.configuration.JwtUtils;
import com.woniu.customer.service.LoginService;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class JwtAuthFilter extends AuthenticatingFilter{
    // token刷新时间,单位：秒
    private static final int tokenRefreshInterval = 60;
    private LoginService loginService;

    //  设置JWT过滤器
    public JwtAuthFilter(LoginService loginService){
        this.loginService = loginService;
        //  设置登录url
        this.setLoginUrl("/admlogin");
    }
    //  过滤器执行前，需在shiroconfig里设置需要经过过滤器的请求
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("前置过滤器");
        //  将request转化为httprequest
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        //  预检请求放行
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) //对于OPTION请求做拦截，不做token校验
            return false;
        return super.preHandle(request, response);
    }
    // 过滤器执行后，需在shiroconfig里设置需要经过过滤器的请求
    @Override
    protected void postHandle(ServletRequest request, ServletResponse response){
        System.out.println("后置过滤器");
        //  设置cors头部，为了处理跨源资源共享（CORS）相关的需求
//        this.fillCorsHeader(WebUtils.toHttp(request), WebUtils.toHttp(response));
        //  过滤器执行后，向请求头添加其已经执行
        request.setAttribute("jwtShiroFilter.FILTERED", true);
    }
    //  用于确定是否允许访问特定的资源，需在shiroconfig里设置需要经过过滤器的请求
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //  登录请求直接放行(根据 this.setLoginUrl("/admlogin"))
        if(this.isLoginRequest(request, response))
            return true;
        //  标记一个请求是否已经通过了某个特定的过滤器链。
        Boolean afterFiltered = (Boolean)(request.getAttribute("jwtShiroFilter.FILTERED"));
        System.out.println("是否通过过滤器链："+afterFiltered);
        //  如果请求已经通过了JWT过滤器链，则直接返回true，表示允许访问
        if( BooleanUtils.isTrue(afterFiltered))
            return true;
        //  未通过则尝试登录
        boolean allowed = false;
        try {
            //  尝试登录，true，则表示用户成功通过身份验证；如果返回 false，则表示身份验证失败。
            allowed = executeLogin(request, response);
            System.out.println("allowed1:"+allowed);
        } catch(IllegalStateException e){ //not found any token
            try {
                System.out.println("allowed2:"+allowed);
                // 异常捕获，发送到expiredJwtException
                request.setAttribute("expiredJwtException", e);
                //将异常分发到/expiredJwtException控制器
                request.getRequestDispatcher("/expiredJwtException").forward(request, response);
            } catch (ServletException ex) {
                throw new RuntimeException(ex);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
//            return false;
//            throw new RuntimeException("Error occurs when login");
//            request.setAttribute("illegalStateException", e);
        }catch (Exception e) {
            throw new RuntimeException("Error occurs when login", e);
        }
        System.out.println("allowed3:"+allowed);
        //  判断是否登录成功
        return allowed || super.isPermissive(mappedValue);
    }

    //  从HTTP请求头中提取JWT令牌，并创建一个AuthenticationToken对象来表示这个令牌。（重写了executeLogin中的createToken）
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse){
        //  获取授权头部,该头部包含token
        String jwtToken = getAuthzHeader(servletRequest);
        //  判断token是否为空且未过期
        if(StringUtils.isNotBlank(jwtToken)&&!JwtUtils.isTokenExpired(jwtToken)){
            //  如果JWT令牌有效，这行代码创建一个新的JWTToken对象，若为null（令牌无效），进入isAccessAllowed的异常处理程序
            JWTToken token = new JWTToken(jwtToken);
            System.out.println("token:"+token);
            return token;}
        return null;
    }
    //  获取授权头部，该头部包含token
    protected String getAuthzHeader(ServletRequest request) {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        // String header = httpRequest.getHeader("x-auth-token");
        String header = httpRequest.getHeader("utoken");
        System.out.println("header："+header);
        return StringUtils.removeStart(header, "Bearer ");
    }

    //  用于处理HTTP请求的Servlet的一部分。它的主要功能是在访问被拒绝时，设置HTTP响应的内容类型、字符编码和状态码。
    //  它还处理跨源资源共享（CORS）相关的HTTP头。（在通过isAccessAllowed之前返回false则进入该方法）
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //  将传入的ServletResponse对象转换为HttpServletResponse对象，以便能够设置HTTP响应的属性。
        HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse);
        //  设置响应的内容类型为UTF-8编码的JSON格式，并设置状态码为402（未授权）。
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json;charset=UTF`-8");
        //  设置响应的状态码为402（未授权）。
        httpResponse.setStatus(HttpStatus.SC_PAYMENT_REQUIRED);
        System.out.println(httpResponse.getStatus());
        //  设置跨源资源共享（CORS）相关的HTTP头。
//        fillCorsHeader(WebUtils.toHttp(servletRequest), httpResponse);
        //  false表示请求被拒绝，不再执行后续过滤器
        return false;
    }

    //  登录成功后，执行后续过滤器
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("登录成功");
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        //  声明一个字符串变量newToken，用于存储新的JWT令牌。
        String newToken = null;
        AdmCustomer admCustomer = null;
        //  判断token是否为JWTToken类型
        if(token instanceof JWTToken){
            //  将token转换为JWTToken类型
            JWTToken jwtToken = (JWTToken)token;
            //  获取当前用户
            admCustomer = (AdmCustomer) subject.getPrincipal();
            request.setAttribute("user", admCustomer);
            System.out.println("用户名"+((AdmCustomer) subject.getPrincipal()).getEmail());
            //  判断是否需要刷新token
            boolean shouldRefresh = shouldTokenRefresh(JwtUtils.getIssuedAt(jwtToken.getToken()));
            System.out.println("old："+jwtToken.getToken());
            //  如果需要刷新token，则生成新的token
            if(shouldRefresh) {
                String salt = "12345";  //  设置密钥盐
                newToken = JwtUtils.sign(admCustomer.getEmail(), salt, 3600);
            }
        }
        if(StringUtils.isNotBlank(newToken)){
            //  如果生成了新的JWT令牌，则将其设置到HTTP响应的头信息中。
            httpResponse.setHeader("Access-Control-Expose-Headers", "xu-auth-token");
            httpResponse.setHeader("xu-auth-token", newToken);}
        return true;
    }

    //  登录失败后,不再执行后续过滤器
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        return false;
    }


    //  判断是否需要刷新token：比较令牌的发行时间和当前时间来确定是否超过了指定的刷新间隔
    protected boolean shouldTokenRefresh(Date issueAt){
        LocalDateTime issueTime = LocalDateTime.ofInstant(issueAt.toInstant(), ZoneId.systemDefault());
        //  当前时间LocalDateTime.now() - token刷新时间 是否在 token生成时间之后
        return LocalDateTime.now().minusSeconds(tokenRefreshInterval).isAfter(issueTime);
    }

//    protected void fillCorsHeader(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
//        //  允许跨域访问
//        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
//        //  允许跨域访问的方法
//        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD");
//        //  允许跨域访问的请求头
//        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
//    }
}
